﻿using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using DKE_Kinect.Entwicklung.Kinect;
using DKE_Kinect.Entwicklung.PolyVox;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;


namespace DKE_Kinect.Entwicklung.Core
{
	/// <summary>
	/// Manager für Input Aktionen zur Steuerung des Programmflusses.
	/// </summary>
	public class InputManager : BaseManager
	{

		#region Fields


		/// <summary>
		/// Bool der angibt ob gerade aufgenommen wird.
		/// </summary>
		private bool pRecording = false;
		/// <summary>
		/// Bool der angibt ob der Manager im Live Modus (false) oder im Recorded Modus (true) ist.
		/// </summary>
		private bool pPlaying = false;
		/// <summary>
		/// Formatter to Save/Load Records
		/// </summary>
		private BinaryFormatter pBFormatter;
		/// <summary>
		/// Stream to access Files
		/// </summary>
		private Stream pStream;
		/// <summary>
		/// Mouse State from last Frame
		/// </summary>
		private MouseState pPrevMouseState;
		/// <summary>
		/// Mouse State des aktuellen Frames
		/// </summary>
		private MouseState pCurrMouseState;
		/// <summary>
		/// Zustand der Tastatur im letzten Frame
		/// </summary>
		private KeyboardState pPrevKeyboardState;
		/// <summary>
		/// Aktueller Zustand der Tastatur
		/// </summary>
		private KeyboardState pCurrKeyboardState;
		/// <summary>
		/// Zustand der Kinect im letzten Frame
		/// </summary>
		private KinectState pPrevKinectState;
		/// <summary>
		/// Aktueller Zustand der Kinect
		/// </summary>
		private KinectState pCurrKinectState;

		#endregion

		#region Properties

		/// <summary>
		/// Gibt den aktuellen KinectState zurück
		/// </summary>
		public KinectState CurrKinectState
		{
			get { return this.pCurrKinectState; }
		}
		/// <summary>
		/// Gibt den vorherigen KinectState zurück
		/// </summary>
		public KinectState PrevKinectState
		{
			get { return this.pPrevKinectState; }
		}

		#endregion

		/// <summary>
		/// Konstruktor. Erstellt eine Instanz des Managers und weist die aktuelle Spielinstanz als Takgeber zu 
		/// </summary>
		/// <param name="game">Aktuelle Spielinstanz</param>
		public InputManager (Application game)
			: base(game)
		{
			pCurrKeyboardState = Keyboard.GetState();
			pCurrMouseState = Mouse.GetState();
			pCurrKinectState = this.pParent.KinectManager.KinectState;
			pBFormatter = new BinaryFormatter();
		}


		/// <summary>
		/// Update Zyklus: Wird kontinuirlich von der Game Klasse getriggert um Logik zu berechnen
		/// </summary>
		/// <param name="_gameTime">Spielzeit Objekt mit Zeit seit letzem Update, Gesamtlaufzeit u.Ä.</param>
		public override void Update (Microsoft.Xna.Framework.GameTime _gameTime)
		{
			//Vorherigen States sichern
			pPrevKeyboardState = pCurrKeyboardState;
			pPrevMouseState = pCurrMouseState;
			pPrevKinectState = pCurrKinectState;

			//Neue States abholen
			pCurrKeyboardState = Keyboard.GetState();
			pCurrMouseState = Mouse.GetState();

			// Wenn im Live-Modus (Kinect an), dann hole KinectState ab
			if (!pPlaying)
			{
				pCurrKinectState = this.pParent.KinectManager.KinectState;

				// Aufnahme --> In Record speichern
				if (pRecording)
				{
					pBFormatter.Serialize(pStream, pCurrKinectState);
				}
			}
			// Sonst (Recorded) versuche Record zu laden
			else
			{
				try
				{
					pCurrKinectState = (KinectState)pBFormatter.Deserialize(pStream);
				}
				catch (SerializationException e)
				{
					Console.WriteLine("Streaming beendet ");
					pStream.Close();
					this.StopPlaying();
					e.ToString();
				}
				catch
				{

				}
			}

			//Update logic hier einfügen
			try
			{

                #region Key-Bindings:

				//KEY O: LeftArmOut simulieren
				if (this.pCurrKeyboardState.IsKeyDown(Keys.O))
				{
					pCurrKinectState.LeftArmOut = true;
				}
				//KEY U: LeftHandUp simulieren
				if (this.pCurrKeyboardState.IsKeyDown(Keys.U))
				{
					pCurrKinectState.LeftHandUp = true;
				}
				//KEY F: Push/LeftArmForward simulieren
				if (this.pCurrKeyboardState.IsKeyDown(Keys.F))
				{
					pCurrKinectState.LeftArmForward = true;
				}
				//KEY Escape: Beenden
				if (this.pCurrKeyboardState.IsKeyDown(Keys.Escape)) this.pParent.Exit();

                //KEYS NUMPAD: Swipes für Menüauswahl
				if (this.pCurrKeyboardState.IsKeyDown(Keys.NumPad4))
				{
					pCurrKinectState.SwipeLeft = true;
				}
                if (this.pCurrKeyboardState.IsKeyDown(Keys.NumPad7))
                {
                    pCurrKinectState.SwipeUpperLeft = true;
                }
                if (this.pCurrKeyboardState.IsKeyDown(Keys.NumPad1))
                {
                    pCurrKinectState.SwipeLowerLeft = true;
                }
				if (this.pCurrKeyboardState.IsKeyDown(Keys.NumPad6))
				{
					pCurrKinectState.SwipeRight = true;
				}
                if (this.pCurrKeyboardState.IsKeyDown(Keys.NumPad9))
                {
                    pCurrKinectState.SwipeUpperRight = true;
                }
                if (this.pCurrKeyboardState.IsKeyDown(Keys.NumPad3))
                {
                    pCurrKinectState.SwipeLowerRight = true;
                }
				if (this.pCurrKeyboardState.IsKeyDown(Keys.NumPad8))
				{
					pCurrKinectState.SwipeUp = true;
				}
				if (this.pCurrKeyboardState.IsKeyDown(Keys.NumPad2))
				{
					pCurrKinectState.SwipeDown = true;
				}
				#endregion

				// Bewegung für Cursor im 3D-Raum berechnen
				Vector3 _cMove = (pPrevKinectState.RightHand - pCurrKinectState.RightHand) / 2;

				//Gesten-Funktion je nach aktuellem Modus
				switch (StateManager.CurrMode)
				{
					case eModus.calibration:
						#region Kalibrierung:

						// Linker Hand hoch --> Bewegungs-Modus
						if (pPrevKinectState.LeftHandUp && !pCurrKinectState.LeftHandUp)
						{
                            StateManager.MoveChangeMode = eModus.painting;
							StateManager.ChangeState(eModus.moving);
						}

						#endregion
						break;

					case eModus.cameraModeOrbital:
                    case eModus.cameraModeLook:
						#region Kamera-Modus:

						// Linke Hand hoch --> Kamera-Modus wechseln
						if (!pPrevKinectState.LeftHandUp && pCurrKinectState.LeftHandUp)
						{
                            if (StateManager.CurrMode == eModus.cameraModeOrbital)
                            {
                                StateManager.ChangeState(eModus.cameraModeLook);
                            }
                            else
                            {
                                StateManager.ChangeState(eModus.cameraModeOrbital);
                            }
						}

						// Linker Arm ausgestreckt --> Hauptmenü
						if (!pPrevKinectState.LeftArmOut && pCurrKinectState.LeftArmOut)
						{
							StateManager.ReferencePoint = pCurrKinectState.RightHand;
							StateManager.ChangeState(eModus.mainmenu);
						}

						// Linker Arm vor --> Quickmenü
						else if (pCurrKinectState.LeftArmForward)
						{
                            StateManager.ReferencePoint = pCurrKinectState.RightHand;
							StateManager.ChangeState(eModus.quickmenu);
						}

                        // Kamera-Position oder Blickrichtung ändern
						if (StateManager.CurrMode == eModus.cameraModeOrbital)
						{
							this.pParent.GrafikManager.Camera.MoveOrbital(this.pParent.GrafikManager.Camera.applyToPlane(_cMove));
						}
                        else if (StateManager.CurrMode == eModus.cameraModeLook)
						{
							this.pParent.GrafikManager.Camera.MoveLookAtTarget(this.pParent.GrafikManager.Camera.applyToPlane(_cMove));
						}

						#endregion
						break;

					case eModus.moving:
						#region Bewegungs-Modus:

						// Linker Arm seitl. ausstrecken --> Hauptmenü
						if (!pPrevKinectState.LeftArmOut && pCurrKinectState.LeftArmOut)
						{
							StateManager.ReferencePoint = pCurrKinectState.RightHand;
							StateManager.ChangeState(eModus.mainmenu);
						}

						// Linke Hand hoch --> Vorheriger Modus
						else if (!pPrevKinectState.LeftHandUp && pCurrKinectState.LeftHandUp)
						{
                            StateManager.ChangeState(StateManager.MoveChangeMode);
						}

						// Linker Arm vor --> Quickmenü
						else if (pCurrKinectState.LeftArmForward)
						{
                            StateManager.ReferencePoint = pCurrKinectState.RightHand;
							StateManager.ChangeState(eModus.quickmenu);
						}

						//Cursor bei Bewegung
						this.pParent.GrafikManager.Cursor.Move(this.pParent.GrafikManager.Camera.applyToPlane(_cMove));

						#endregion
						break;

					case eModus.painting:
						#region Zeichen-Modus:

						// Linker Arm seitl. ausstrecken --> Hauptmenü
						if (!pPrevKinectState.LeftArmOut && pCurrKinectState.LeftArmOut)
						{
							StateManager.ReferencePoint = pCurrKinectState.RightHand;
							StateManager.ChangeState(eModus.mainmenu);
						}

						// Linke Hand hoch --> Bewegungs-Modus
						else if (!pPrevKinectState.LeftHandUp && pCurrKinectState.LeftHandUp)
						{
                            StateManager.MoveChangeMode = eModus.painting;
							StateManager.ChangeState(eModus.moving);
						}

						// Linker Arm vor --> Quickmenü
						else if (pCurrKinectState.LeftArmForward)
						{
							StateManager.ReferencePoint = pCurrKinectState.RightHand;
							StateManager.ChangeState(eModus.quickmenu);
						}

						//Cursor bei Zeichnen
						this.pParent.GrafikManager.Cursor.Move(this.pParent.GrafikManager.Camera.applyToPlane(_cMove));
						Application.PolyvoxManager.DrawLine(this.pParent.GrafikManager.Cursor.PrevPosition, this.pParent.GrafikManager.Cursor.Position);
                        
                        #endregion
						break;

					case eModus.eraser:
						#region Radiergummi-Modus:

						// Linker Arm seitl. ausstrecken --> Hauptmenü
						if (!pPrevKinectState.LeftArmOut && pCurrKinectState.LeftArmOut)
						{
							StateManager.ReferencePoint = pCurrKinectState.RightHand;
							StateManager.ChangeState(eModus.mainmenu);
						}

						// Linke Hand hoch --> Bewegungs-Modus
						else if (!pPrevKinectState.LeftHandUp && pCurrKinectState.LeftHandUp)
						{
                            StateManager.MoveChangeMode = eModus.eraser;
							StateManager.ChangeState(eModus.moving);
						}

						// Linker Arm vor --> Quickmenü
						else if (pCurrKinectState.LeftArmForward)
						{
                            StateManager.ReferencePoint = pCurrKinectState.RightHand;
							StateManager.ChangeState(eModus.quickmenu);
						}

                        this.pParent.GrafikManager.Cursor.Move(this.pParent.GrafikManager.Camera.applyToPlane(_cMove));
						Application.PolyvoxManager.DrawLine(this.pParent.GrafikManager.Cursor.PrevPosition, this.pParent.GrafikManager.Cursor.Position);

						#endregion
						break;

					case eModus.mainmenu:
						#region Hauptmenü:

						// Linke Hand zur Seite --> Hauptmenü aus
						if (!pPrevKinectState.LeftArmOut && pCurrKinectState.LeftArmOut)
						{
							StateManager.ReturnToPrevState();
						}
						else
						{

							// Swipe Links --> Ebene zurück
							if ((!pPrevKinectState.SwipeLeft && pCurrKinectState.SwipeLeft))
							{
								this.pParent.GrafikManager.MainMenu.LayerBackward();
							}

							// Swipe Rechts --> Ebene weiter/bestätigen
							else if ((!pPrevKinectState.SwipeRight && pCurrKinectState.SwipeRight) || (!pPrevKinectState.LeftHandUp && pCurrKinectState.LeftHandUp))
							{
								this.pParent.GrafikManager.MainMenu.PerformAction();
							}

							// Swipe Hoch --> Element nach oben
							else if (!pPrevKinectState.SwipeUp && pCurrKinectState.SwipeUp)
							{
								this.pParent.GrafikManager.MainMenu.Previous();
							}

							// Swipe Runter --> Element nach unten
							else if (!pPrevKinectState.SwipeDown && pCurrKinectState.SwipeDown)
							{
								this.pParent.GrafikManager.MainMenu.Next();
							}
						}
						#endregion
						break;

					case eModus.quickmenu:
						#region Quickmenü:

						// Linke Hand nicht mehr vor --> Quickmenü schließen und aktuellen Auswahlpunkt aktivieren
						if (!pCurrKinectState.LeftArmForward)
						{
                            this.pParent.GrafikManager.QuickMenu.PerformAction();
							StateManager.ReturnToPrevState();
						}

						else
						{
                            // Swipe Links Hoch
							if (pCurrKinectState.SwipeUpperLeft)
							{
								this.pParent.GrafikManager.QuickMenu.ChooseTopLeft();
							}

							// Swipe Rechts Hoch
							else if (pCurrKinectState.SwipeUpperRight)
							{
                                this.pParent.GrafikManager.QuickMenu.ChooseTopRight();
							}

							// Swipe Hoch 
							else if (pCurrKinectState.SwipeUp)
							{
                                this.pParent.GrafikManager.QuickMenu.ChooseTop();
							}

                            // Swipe Links Runter
                            if (pCurrKinectState.SwipeLowerLeft)
                            {
                                this.pParent.GrafikManager.QuickMenu.ChooseBottomLeft();
                            }

                            // Swipe Rechts Runter
                            else if (pCurrKinectState.SwipeLowerRight)
                            {
                                this.pParent.GrafikManager.QuickMenu.ChooseBottomRight();
                            }

							// Swipe Runter 
							else if (pCurrKinectState.SwipeDown)
							{
                                this.pParent.GrafikManager.QuickMenu.ChooseBottom();
							}
						}
						#endregion
						break;

                    case eModus.colormenu:
                        #region Farbwahl:

                        // // Linke Hand raus --> Zurück
                        if (!pPrevKinectState.LeftArmOut && pCurrKinectState.LeftArmOut)
                        {
                            StateManager.ReturnToPrevState();
                        }
                        else
                        {
                            // Swipe Links --> Element zurück
                            if ((!pPrevKinectState.SwipeLeft && pCurrKinectState.SwipeLeft))
                            {
                                this.pParent.GrafikManager.ColorMenu.Left();
                            }

                            // Swipe Rechts --> Element weiter
                            else if (!pPrevKinectState.SwipeRight && pCurrKinectState.SwipeRight)
                            {
                                this.pParent.GrafikManager.ColorMenu.Right();
                            }

                            // Swipe Hoch --> Element nach oben
                            else if (!pPrevKinectState.SwipeUp && pCurrKinectState.SwipeUp)
                            {
                                this.pParent.GrafikManager.ColorMenu.Up();
                            }

                            // Swipe Runter --> Element nach unten
                            else if (!pPrevKinectState.SwipeDown && pCurrKinectState.SwipeDown)
                            {
                                this.pParent.GrafikManager.ColorMenu.Down();
                            }
                            
                            // Linke Hand Hoch --> Auswählen
                            else if (!pPrevKinectState.LeftHandUp && pCurrKinectState.LeftHandUp)
                            {
                                Voxel.SetColor(this.pParent.GrafikManager.ColorMenu.Choose());
                            }

                        }
                        #endregion
                        break;

                    case eModus.savemenu:
                        #region Speichern:

                        // Linke Hand raus --> Zurück
                        if (!pPrevKinectState.LeftArmOut && pCurrKinectState.LeftArmOut)
                        {
                            StateManager.ReturnToPrevState();
                        }
                        else
                        {
                            // Swipe Links --> Element zurück
                            if ((!pPrevKinectState.SwipeLeft && pCurrKinectState.SwipeLeft))
                            {
                                this.pParent.GrafikManager.ProjectMenu.Left();
                            }

                            // Swipe Rechts --> Element weiter
                            else if (!pPrevKinectState.SwipeRight && pCurrKinectState.SwipeRight)
                            {
                                this.pParent.GrafikManager.ProjectMenu.Right();
                            }

                            // Swipe Hoch --> Element nach oben
                            else if (!pPrevKinectState.SwipeUp && pCurrKinectState.SwipeUp)
                            {
                                this.pParent.GrafikManager.ProjectMenu.Up();
                            }

                            // Swipe Runter --> Element nach unten
                            else if (!pPrevKinectState.SwipeDown && pCurrKinectState.SwipeDown)
                            {
                                this.pParent.GrafikManager.ProjectMenu.Down();
                            }

                            // Linke Hand Hoch --> Auswählen
                            else if (!pPrevKinectState.LeftHandUp && pCurrKinectState.LeftHandUp)
                            {
                                this.pParent.GrafikManager.ProjectMenu.ChooseToSave();
                            }

                        }
                        #endregion
                        break;

                    case eModus.loadmenu:
                        #region Laden:

                        // Linke Hand raus --> Zurück
                        if (!pPrevKinectState.LeftArmOut && pCurrKinectState.LeftArmOut)
                        {
                            StateManager.ReturnToPrevState();
                        }
                        else
                        {
                            // Swipe Links --> Element zurück
                            if ((!pPrevKinectState.SwipeLeft && pCurrKinectState.SwipeLeft))
                            {
                                this.pParent.GrafikManager.ProjectMenu.Left();
                            }

                            // Swipe Rechts --> Element weiter
                            else if (!pPrevKinectState.SwipeRight && pCurrKinectState.SwipeRight)
                            {
                                this.pParent.GrafikManager.ProjectMenu.Right();
                            }

                            // Swipe Hoch --> Element nach oben
                            else if (!pPrevKinectState.SwipeUp && pCurrKinectState.SwipeUp)
                            {
                                this.pParent.GrafikManager.ProjectMenu.Up();
                            }

                            // Swipe Runter --> Element nach unten
                            else if (!pPrevKinectState.SwipeDown && pCurrKinectState.SwipeDown)
                            {
                                this.pParent.GrafikManager.ProjectMenu.Down();
                            }

                            // Linke Hand Hoch --> Auswählen
                            else if (!pPrevKinectState.LeftHandUp && pCurrKinectState.LeftHandUp)
                            {
                                this.pParent.GrafikManager.ProjectMenu.ChooseToLoad();
                            }

                        }
                        #endregion
                        break;
				}
			}
			catch (NullReferenceException e)
			{
				Console.WriteLine(e.Message);
			}

		}

		/// <summary>
		/// Prüft, ob eine Taste gedrückt wurde.
		/// </summary>
		/// <param name="Key">Prüfende Taste.</param>
		/// <returns></returns>
		private bool KeyPressed (Keys Key)
		{
			return this.KeyPressed(this.pCurrKeyboardState, this.pPrevKeyboardState, Key);
		}
		/// <summary>
		/// Prüft, ob eine Taste gedrückt wurde.
		/// </summary>
		/// <param name="Current">Jetziger Keyboard - State</param>
		/// <param name="Previous">Alter Keyboard - State</param>
		/// <param name="Key">Prüfende Taste.</param>
		/// <returns></returns>
		private bool KeyPressed (KeyboardState Current, KeyboardState Previous, Keys Key)
		{
			return (Current.IsKeyUp(Key) && Previous.IsKeyDown(Key));
		}

		/// <summary>
		/// Draw Zyklus: Wird kontinuirlich getrigger von der Game Klasse. Dient zum zeichnen des Screens
		/// </summary>
		/// <param name="_gameTime">Spielzeit Objekt mit Zeit seit letzem Update, Gesamtlaufzeit u.Ä.</param>
		public override void Draw (Microsoft.Xna.Framework.GameTime _gameTime)
		{
			throw new NotImplementedException();
		}

		#region Recorder
		/// <summary>
		/// Startet das Aufnehmen der Kinect Daten
		/// </summary>
		/// <param name="Filename">Name der Record File</param>
		public void StartRecording (String Filename)
		{
			if (!pRecording)
			{
				pStream = File.Open("../../../../DKE_KinectContent/records/" + Filename + ".rdf", FileMode.Create);
				pRecording = true;
			}
			else
			{
				throw new DkeException("Already Recording");
			}
		}
		/// <summary>
		/// Beendet das Aufnehmen 
		/// </summary>
		public void StopRecording ()
		{
			if (pRecording)
			{
				pStream.Close();
				pRecording = false;
			}
		}
		/// <summary>
		/// Stoppt das Abspielen eines Records
		/// </summary>
		public void StopPlaying ()
		{
			StatusInfo.SetKinectDataStatus();
			pPlaying = false;
		}

		/// <summary>
		/// Startet das Abspielen einer Record Datei
		/// </summary>
		/// <param name="_recordName">Abzuspielende Record Datei</param>
		public void StartPlaying (String _recordName)
		{
			StatusInfo.SetKinectDataStatus(eKinectStatus.playback);
			pStream = File.Open("../../../../DKE_KinectContent/records/" + _recordName + ".rdf", FileMode.Open);
			pPlaying = true;
		}
		#endregion

        private void SaveGame(String filename)
        {
            SaveLoad.SaveGameFile(filename, Application.CurrentProjekt, Application.PolyvoxManager.VolumeRecord);
        }

        private void LoadGame(String filename)
        {
            SaveLoad.LoadGameFile(filename);
        }
	}
}
